Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Effects list refactor continued: passive effects traversal #19374

Merged
merged 11 commits into from
Jul 29, 2020

Conversation

bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Jul 15, 2020

  • Adds new Passive subtree tag value.
  • Bubbles passive flag to ancestors in the case of an unmount.
  • Adds recursive traversal for passive effects (mounts and unmounts).
  • Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop.
  • Re-adds sibling and child pointer detaching (temporarily removed in previous PR).
  • Addresses some minor TODO comments left over from previous PRs.

Co-authored-by: Luna Ruan [email protected]

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jul 15, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Jul 15, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit ae37c7c:

Sandbox Source
React Configuration

@sizebot
Copy link

sizebot commented Jul 15, 2020

Details of bundled changes.

Comparing: 0eea166...ae37c7c

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js 0.0% 0.0% 924.18 KB 924.27 KB 210.05 KB 210.1 KB NODE_DEV
ReactDOMForked-prod.js 🔺+0.6% 🔺+0.5% 397.32 KB 399.78 KB 73.79 KB 74.13 KB FB_WWW_PROD
react-dom.production.min.js 0.0% 0.0% 124.24 KB 124.24 KB 39.84 KB 39.84 KB NODE_PROD
ReactDOMForked-profiling.js +0.7% +0.6% 412.33 KB 415.28 KB 76.43 KB 76.87 KB FB_WWW_PROFILING
react-dom-server.node.production.min.js 0.0% 0.0% 20.72 KB 20.72 KB 7.68 KB 7.68 KB NODE_PROD
ReactDOMTesting-dev.js 0.0% 0.0% 959.55 KB 959.64 KB 215.13 KB 215.18 KB FB_WWW_DEV
react-dom-test-utils.production.min.js 0.0% 0.0% 12.71 KB 12.71 KB 4.97 KB 4.97 KB NODE_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 5.36 KB 5.36 KB 1.8 KB 1.8 KB UMD_DEV
react-dom-unstable-fizz.node.production.min.js 0.0% 🔺+0.2% 1.17 KB 1.17 KB 664 B 665 B NODE_PROD
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.3% 1.2 KB 1.2 KB 703 B 705 B UMD_PROD
react-dom.development.js 0.0% 0.0% 970.76 KB 970.85 KB 212.61 KB 212.65 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.3% 1.01 KB 1.01 KB 614 B 616 B NODE_PROD
react-dom.production.min.js 0.0% 0.0% 124.1 KB 124.1 KB 40.58 KB 40.58 KB UMD_PROD
react-dom.profiling.min.js 0.0% 0.0% 129.27 KB 129.27 KB 42.2 KB 42.21 KB UMD_PROFILING
ReactDOMForked-dev.js +0.5% +0.3% 997.74 KB 1002.37 KB 222.18 KB 222.93 KB FB_WWW_DEV
react-dom.profiling.min.js 0.0% 0.0% 129.62 KB 129.62 KB 41.46 KB 41.46 KB NODE_PROFILING
react-dom-server.browser.production.min.js 0.0% 0.0% 20.38 KB 20.38 KB 7.54 KB 7.54 KB UMD_PROD
ReactDOM-dev.js 0.0% 0.0% 994.4 KB 994.5 KB 221.92 KB 221.97 KB FB_WWW_DEV
ReactDOM-prod.js 0.0% 0.0% 395.86 KB 395.86 KB 73.5 KB 73.51 KB FB_WWW_PROD
react-dom-server.browser.development.js 0.0% 0.0% 137.6 KB 137.6 KB 36.47 KB 36.47 KB NODE_DEV
ReactDOM-profiling.js 0.0% 0.0% 410.26 KB 410.26 KB 76.13 KB 76.14 KB FB_WWW_PROFILING
react-dom-server.browser.production.min.js 0.0% 0.0% 20.3 KB 20.3 KB 7.52 KB 7.52 KB NODE_PROD
ReactDOMServer-dev.js 0.0% 0.0% 143.44 KB 143.44 KB 36.48 KB 36.48 KB FB_WWW_DEV
ReactDOMServer-prod.js 0.0% 0.0% 46.68 KB 46.68 KB 10.92 KB 10.93 KB FB_WWW_PROD
react-dom-test-utils.development.js 0.0% -0.0% 65.91 KB 65.91 KB 18.18 KB 18.18 KB UMD_DEV

Size changes (experimental)

Generated by 🚫 dangerJS against ae37c7c

@sizebot
Copy link

sizebot commented Jul 15, 2020

Details of bundled changes.

Comparing: 0eea166...ae37c7c

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js 0.0% 0.0% 887.07 KB 887.16 KB 203.25 KB 203.29 KB NODE_DEV
ReactDOMForked-prod.js 🔺+0.6% 🔺+0.4% 408.45 KB 410.92 KB 75.44 KB 75.75 KB FB_WWW_PROD
react-dom.production.min.js 0.0% 0.0% 119.45 KB 119.45 KB 38.39 KB 38.39 KB NODE_PROD
ReactDOMForked-profiling.js +0.7% +0.5% 423.53 KB 426.48 KB 78.07 KB 78.45 KB FB_WWW_PROFILING
react-dom-server.node.production.min.js 0.0% 0.0% 20.26 KB 20.26 KB 7.6 KB 7.6 KB NODE_PROD
react-dom-test-utils.production.min.js 0.0% 0.0% 12.84 KB 12.84 KB 5.03 KB 5.03 KB UMD_PROD
ReactDOMTesting-dev.js 0.0% 0.0% 987.71 KB 987.81 KB 220.54 KB 220.58 KB FB_WWW_DEV
react-dom-test-utils.development.js 0.0% -0.0% 60.88 KB 60.88 KB 17.65 KB 17.65 KB NODE_DEV
react-dom.development.js 0.0% 0.0% 931.85 KB 931.94 KB 205.74 KB 205.79 KB UMD_DEV
react-dom.profiling.min.js 0.0% 0.0% 123.25 KB 123.25 KB 40.28 KB 40.28 KB UMD_PROFILING
ReactDOMForked-dev.js +0.5% +0.3% 1023.28 KB 1 MB 226.88 KB 227.63 KB FB_WWW_DEV
react-dom-server.browser.production.min.js 0.0% 0.0% 19.92 KB 19.92 KB 7.46 KB 7.46 KB UMD_PROD
ReactDOM-dev.js 0.0% 0.0% 1019.95 KB 1020.04 KB 226.57 KB 226.62 KB FB_WWW_DEV
ReactDOM-profiling.js 0.0% -0.0% 421.45 KB 421.45 KB 77.75 KB 77.75 KB FB_WWW_PROFILING
react-dom-server.browser.production.min.js 0.0% 0.0% 19.84 KB 19.84 KB 7.44 KB 7.44 KB NODE_PROD
ReactDOMServer-prod.js 0.0% 0.0% 47.54 KB 47.54 KB 11.14 KB 11.14 KB FB_WWW_PROD

ReactDOM: size: 0.0%, gzip: 0.0%

Size changes (stable)

Generated by 🚫 dangerJS against ae37c7c

@gaearon
Copy link
Collaborator

gaearon commented Jul 17, 2020

Is the delta link out of date? I see no changes.

@bvaughn
Copy link
Contributor Author

bvaughn commented Jul 17, 2020

@gaearon Whoops, the other two PRs have landed now 😄 You can just view it normally (although please keep in mind it's not ready for review, just a draft). I'll delete that line though.

@bvaughn bvaughn force-pushed the effects_list_refactor_passive_effects branch from 9b894fe to c536679 Compare July 22, 2020 15:22
@bvaughn bvaughn marked this pull request as ready for review July 22, 2020 15:24
@bvaughn bvaughn changed the title Effects list refactor continued: passive effects traversal [DRAFT] Effects list refactor continued: passive effects traversal Jul 22, 2020
@bvaughn
Copy link
Contributor Author

bvaughn commented Jul 22, 2020

Okay. This PR is ready for review now 😄

@bvaughn bvaughn force-pushed the effects_list_refactor_passive_effects branch from 352b88b to d96907c Compare July 22, 2020 19:54
@bvaughn bvaughn force-pushed the effects_list_refactor_passive_effects branch from d96907c to 9b91189 Compare July 24, 2020 14:28
@bvaughn bvaughn force-pushed the effects_list_refactor_passive_effects branch from 9b91189 to 157aa81 Compare July 27, 2020 17:14
@bvaughn bvaughn requested a review from acdlite July 27, 2020 17:24
@bvaughn bvaughn force-pushed the effects_list_refactor_passive_effects branch 3 times, most recently from 5b6bc9d to 15288ed Compare July 28, 2020 20:53
@bvaughn
Copy link
Contributor Author

bvaughn commented Jul 28, 2020

@acdlite @lunaruan I think 15288ed is the change we discussed earlier.

@bvaughn bvaughn requested a review from lunaruan July 28, 2020 20:57
// mark the parent so that we're sure to traverse after commit and run any unmount functions.
const primaryEffectTag = childToDelete.effectTag & PassiveMask;
const primarySubtreeTag = childToDelete.subtreeTag & PassiveSubtreeTag;
if (primaryEffectTag !== NoEffect || primarySubtreeTag !== NoSubtreeTag) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this optimization isn't worth it and we should always set the PassiveSubtreeTag on the parent when a child is deleted. i.e. add Deletion to PassiveMask.

Effects are very common — any large tree is almost certain to have at least one. So the optimization really only kicks in when you delete a leaf node. Not sure it's worth the extra code.

I'm almost tempted to say that PassiveStatic isn't worth it for the same reason; but since we're planning to have other static tags for things like portals, PassiveStatic doesn't add any net additional overhead, so might as well keep it.

What do you think?

Copy link
Contributor Author

@bvaughn bvaughn Jul 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this optimization isn't worth it and we should always set the PassiveSubtreeTag on the parent when a child is deleted.

Given that the concept of static flags seems like a positive– and so keeping PassiveStatic around doesn't add any net overhead– the only question seems to be whether the few extra bytes required for us to check effectTag and subtreeTag are worth it, yeah?

With the current check, we might over traverse in the event that a child was deleted that had a passive effect, but no cleanup function. (Seems unlikely.) If we removed this check, we might over traverse any time a leaf node is deleted, period. Still, I guess that wouldn't be that bad since we'd only traverse the path to where the leaf was deleted.

TBH I think the cost is pretty small on either side (number or bytes or potential over traversing). Happy to remove the check for now.

cba61f7

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we removed this check, we might over traverse any time a leaf node is deleted, period. Still, I guess that wouldn't be that bad since we'd only traverse the path to where the leaf was deleted.

Yeah I think that's fine. It's also related to the detachFiber discussion. Since we're moving most of that work to the passive phase, then we need to visit every deletion during the passive phase anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point I guess. I hadn't considered that aspect.

I consider the "detach" methods as kind of an optional optimization.

// Note that we can't clear child or sibling pointers yet,
// because they may be required for passive effects.
// These pointers will be cleared in a separate pass.
// Ideally, we should clear the child pointer of the parent alternate to let this
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a Deletion always gets a passive effect scheduled on it, as suggested above, we could move this whole function to the passive phase. (Except maybe return and alternate, since those affect semantics, like if you call setState on an unmounted component.)

Copy link
Contributor Author

@bvaughn bvaughn Jul 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't clear return during layout, it would break a few things:

  • The unmounted state warning
  • How events bubble for the new API Dominic has been working on

That being said, it would fix a limitation of the Profiler API when it comes to measuring time spent in passive effect cleanup. That time currently doesn't get reported during an unmount (either a regular unmount or because of an error boundary) because the return pointer has been detached previously and so it can't bubble up to the nearest Profiler node. If we deferred detaching it until after passive, that time would be reported which is a plus.

Not sure if there would be a viable workaround for the event case, but for the unmounted state warning- we could walk the return path and look for Fibers with Deletion effects scheduled, except that this would have false positives for deletions that were scheduled and not committed so that's a bummer.

I guess we could always set another bit to mark a Fiber as having been deleted but at that point we aren't really saving much (aside from the Profiler fix).

Copy link
Contributor Author

@bvaughn bvaughn Jul 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That being said, I believe we can safely defer everything except the return pointer cleanup to the passive effects phase which would be slightly better.

7d618ed

Brian Vaughn added 3 commits July 29, 2020 11:30
* Adds new Passive subtree tag value.
* Bubbles passive flag to ancestors in the case of an unmount.
* Adds recursive traversal for passive effects (mounts and unmounts).
* Removes pendingPassiveHookEffectsMount and pendingPassiveHookEffectsUnmount arrays from work loop.
* Re-adds sibling and child pointer detaching (temporarily removed in previous PR).
* Addresses some minor TODO comments left over from previous PRs.
This allows certain concepts to persist without recalculting them, e.g. whether a subtree contains passive effects or portals. This helps for cases like nested unmounts that contain deep passive effects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants